import type { Bit as _Bit, IEMPTY_OBJ, SBit as _SBit } from '@/utils/constant'
import type { AxiosRequestConfig, Method } from 'axios'

import axios from 'axios'

export namespace R {
  export type Nullable<T = never> = T | void | null
  export interface API<VO = any, DTO = any> {
    (params: DTO, ...args: any[]): Promise<VO>
    url: string
    method: Method
  }
  export interface IData<T = IEMPTY_OBJ> {
    code: string
    msg: string
    data: T
  }
  export type Bit = _Bit
  export type SBit = _SBit
  export type IParams<T extends { (dto: any): Promise<unknown> }> = T extends {
    (dto: infer D): Promise<unknown>
  }
    ? D
    : IEMPTY_OBJ

  export type IResponse<T extends { (dto: any): Promise<unknown> }> =
    T extends { (dto: any): Promise<infer V> } ? V : Data

  export type IPageDTO<
    T extends Record<string, unknown> = Record<string, unknown>
    > = {
      /** 请求页码 */
      pageNo: number
      /** 每页条数 */
      pageSize: number
    } & T
  export interface IPageVO<T extends object = Record<string, unknown>> {
    /** 当前页(没有写在文档上) */
    current: number
    /** 页码大小(没有写在文档上) */
    size: number
    /** 总条数 */
    total: number
    /** 总页数 */
    pages: number
    records: T[]
  }

  /** 通用结构：上传 EXCEL 解析结果 */
  export interface IUpExcelVO {
    requestId: string
    successNum: number
    failNum: number
    errorData: ReadonlyArray<{
      id: string
      errorReason: string
      errorNum: number
      errorName: string
    }>
  }
  export interface IUpExcelDTO {
    file: File
    /** 传入 request Id:
     *    在一些场景，如上传错误的情况下，再次上传的时候传入requestId，
     *    后台根据传入的requestId可以选择更新之前的上传记录,不传的统一当作新增处理
     */
    requestId?: string
  }

  type Data<T = void> = T extends Nullable ? IEMPTY_OBJ : T

  export function del<VO, DTO = void>(url: string) {
    const api: API<Data<VO>, DTO> = (
      params?: DTO,
      opts?: AxiosRequestConfig
    ) => {
      return axios.delete<void, Data<VO>>(url, { data: params, ...opts })
    }
    api.method = 'get'
    api.url = url
    return api
  }
  export function delQuery<VO, DTO = void>(url: string) {
    const api: API<Data<VO>, DTO> = (params?: DTO, opts?: AxiosRequestConfig) =>
      axios.delete<void, Data<VO>>(url, { params, ...opts })
    api.method = 'get'
    api.url = url
    return api
  }
  export function get<VO, DTO = void>(url: string, opts?: AxiosRequestConfig) {
    const api: API<Data<VO>, DTO> = (
      params?: DTO,
      _opts?: AxiosRequestConfig
    ) => {
      return axios.get<void, Data<VO>>(url, { params, ...opts, ..._opts })
    }
    api.method = 'get'
    api.url = url
    return api
  }
  export function post<VO, DTO = void>(url: string, opts?: AxiosRequestConfig) {
    const api: API<Data<VO>, DTO> = (params?: DTO) =>
      axios.post<void, Data<VO>>(url, params, opts)
    api.method = 'post'
    api.url = url
    return api
  }
  export function postForm<VO, DTO = void>(
    url: string,
    opts?: AxiosRequestConfig
  ) {
    const api: API<Data<VO>, DTO> = (params?: DTO) => {
      const isUrlEncode = (opts && opts.headers && /application\/x-www-form-urlencoded/.test(opts.headers['content-type'] as string))
      return axios.postForm<void, Data<VO>>(url, isUrlEncode ? new URLSearchParams(params as any) : params, opts)
    }
    api.method = 'post'
    api.url = url
    return api
  }
  export function postQuery<VO, DTO = void>(
    url: string,
    opts?: AxiosRequestConfig
  ) {
    const api: API<Data<VO>, DTO> = (params?: DTO) =>
      axios.post<void, Data<VO>>(url, void 0, { ...opts, params })
    api.method = 'post'
    api.url = url
    return api
  }
  export function put<VO, DTO = void>(url: string, opts?: AxiosRequestConfig) {
    const api: API<Data<VO>, DTO> = (params?: DTO) =>
      axios.put<void, Data<VO>>(url, params, opts)
    api.method = 'put'
    api.url = url
    return api
  }
  export function putForm<VO, DTO = void>(
    url: string,
    opts?: AxiosRequestConfig
  ) {
    const api: API<Data<VO>, DTO> = (params?: DTO) =>
      axios.putForm<void, Data<VO>>(url, params, opts)
    api.method = 'put'
    api.url = url
    return api
  }
  export function putQuery<VO, DTO = void>(
    url: string,
    opts?: AxiosRequestConfig
  ) {
    const api: API<Data<VO>, DTO> = (params?: DTO) =>
      axios.put<void, Data<VO>>(url, void 0, { ...opts, params })
    api.method = 'put'
    api.url = url
    return api
  }
  export function patch<VO, DTO = void>(url: string, opts: AxiosRequestConfig) {
    const api: API<Data<VO>, DTO> = (params?: DTO) =>
      axios.patch<void, Data<VO>>(url, params, opts)
    api.method = 'patch'
    api.url = url
    return api
  }
  export function patchForm<VO, DTO = void>(
    url: string,
    opts: AxiosRequestConfig
  ) {
    const api: API<Data<VO>, DTO> = (params?: DTO) =>
      axios.patchForm<void, Data<VO>>(url, params, opts)
    api.method = 'patch'
    api.url = url
    return api
  }
  export function patchQuery<VO, DTO = void>(
    url: string,
    opts: AxiosRequestConfig
  ) {
    const api: API<Data<VO>, DTO> = (params?: DTO) =>
      axios.patch<void, Data<VO>>(url, void 0, { ...opts, params })
    api.method = 'patch'
    api.url = url
    return api
  }
}

export default R
